// Package board implements a chess board.
package board

import (
	"fmt"
)

// File represents a file of the board.
type File uint8

// All definded files on the board. NoFile represents the zero value of a File.
const (
	NoFile File = iota
	A
	B
	C
	D
	E
	F
	G
	H
)

var files = []string{
	NoFile: "NoFile", A: "a", B: "b", C: "c", D: "d", E: "e", F: "f", G: "g", H: "h",
}

// String returns the file as lower case letter, as used in algebraic notation.
func (this File) String() string {
	return files[this]
}

// Rank represents a rank on the board.
type Rank uint8

// All definded Ranks on the board. NoRank is the zero value of Rank.
const (
	NoRank Rank = iota
	Rank1
	Rank2
	Rank3
	Rank4
	Rank5
	Rank6
	Rank7
	Rank8
)

var ranks = []string{
	NoRank: "NoRank",
	Rank1:  "1", Rank2: "2", Rank3: "3", Rank4: "4",
	Rank5: "5", Rank6: "6", Rank7: "7", Rank8: "8",
}

// String returns the number of the rank, as used in algebraic notation.
func (this Rank) String() string {
	return ranks[this]
}

// RowsBoard and ColumnsBoard are the dimensions of the board.
const (
	RowsBoard    = 8
	ColumnsBoard = 8
)

// size of the virtual board
const (
	rowsInternalBoard    = RowsBoard + borderTop + borderBottom
	columnsInternalBoard = ColumnsBoard + borderLeft + borderRight
)

// Square represents a square on the board.
type Square uint8

// NoSquare is the zero value of Square.
const (
	NoSquare Square = 0x0
)

// All squares of the board.
const (
	A1 Square = iota + iota/ColumnsBoard*(borderRight+borderLeft) +
		2*columnsInternalBoard + borderLeft
	B1
	C1
	D1
	E1
	F1
	G1
	H1
	A2
	B2
	C2
	D2
	E2
	F2
	G2
	H2
	A3
	B3
	C3
	D3
	E3
	F3
	G3
	H3
	A4
	B4
	C4
	D4
	E4
	F4
	G4
	H4
	A5
	B5
	C5
	D5
	E5
	F5
	G5
	H5
	A6
	B6
	C6
	D6
	E6
	F6
	G6
	H6
	A7
	B7
	C7
	D7
	E7
	F7
	G7
	H7
	A8
	B8
	C8
	D8
	E8
	F8
	G8
	H8
)

// BiggestSquare represents the upper bound for square values on the virtual board.
const (
	BiggestSquare Square = rowsInternalBoard*columnsInternalBoard - 1
)

// BitSizeSquare and BitMaskSquare give essential informations to store a Square
// into a bitfield.
const (
	BitSizeSquare = 8
	BitMaskSquare = 1<<BitSizeSquare - 1
)

// Directions on the board, seen from the bottom (white) side.
const (
	Up    = A2 - A1
	Down  = 1<<8 - 1 - Up + 1
	Right = B1 - A1
	Left  = 1<<8 - 1 - Right + 1
)

// GetSquare returns the square on the given rank and file.
func GetSquare(file File, rank Rank) Square {
	return A1 + Square(rank-Rank1)*columnsInternalBoard + Square(file-A)
}

// IsValid returns true if the square is on the board.
//
// This function is slow, use border pieces for fast square validation.
func IsValid(square Square) bool {
	file := File(square-A1)%columnsInternalBoard + A
	rank := Rank(square-A1)/columnsInternalBoard + Rank1
	return rank >= Rank1 && rank <= Rank8 && file >= A && file <= H
}

// String returns an algebraic notation for valid squares.
// For invalid squares a very short description of the square is returned.
func (this Square) String() string {
	switch {
	case this == NoSquare:
		return "NoSquare"
	case !IsValid(this):
		return fmt.Sprintf("Square(%d)", this)
	default:
		return fmt.Sprintf("%s%s", GetFile(this), GetRank(this))
	}
}
